#ifndef __MESSAGEQUEUE_H__
#define __MESSAGEQUEUE_H__

/////////////////////////////////////////////////////////////////////////////
// MessageQueue
/////////////////////////////////////////////////////////////////////////////



// Breif Description of Theory of Operation:
//
// Basicly this is a producer and consumer thread.
// The producer is the push and the consumer is the pop.
// There are two key items being used here, the QMutex and
// the QWaitCondition.
//
// Probably, the pop function will be called first.
// It will enter into a wait condition: 
//
//    mWait.wait(mpMutex, timeout);
//
// In order for this to work, the mutex must be locked prior to making
// this call.  After making this call the mutex will be unlocked.
//
// When a call to 
//
//  mWait.wakeAll()
//
// is made or if the timeout happens this wait condition will 
// be satisfied and the blocking will end.
//
// In push, the mutex is locked, the message is pushed onto the queue
// and then wakeAll() is called, thus satisfying the wait condition.
//



#include <QByteArray>
#include <QMutex>
#include <QWaitCondition>
#include <QQueue>


template <class T >
class MessageQueue
{

public:


MessageQueue( T* parent)
{
    mParent = parent;
    mpMutex = new QMutex;
    mResetting = false;
    mQueueEnabled = false;
}


~MessageQueue()
{
    mWait.wakeAll();

    mpMutex->lock();

    delete mpMutex;
}

void clear()
{
   
    mpMutex->lock();
    //mQueue.setAutoDelete(true);
    mQueue.clear();
    //mQueue.setAutoDelete(false);
    mpMutex->unlock();
}



/// This will block until a value is available on the queue or until
/// timeout expires.
/// @param timeout A value >= 0 will cause pop to wait for timeout ms,
///     a value of < 0 will cause timeout to wait indefinitely.
/// @return Returns a valid QByteArray pointer if data is popped, if the
///     timeout expires, or resetBegin/resetEnd is called, then null is
///     returned.

QByteArray pop(unsigned long timeout = ULONG_MAX)
{
    QByteArray result;
    result = 0;
    mpMutex->lock();


    // If we are resetting then we don't want to be giving
    // back messages!
    if(mResetting)
    {
        mpMutex->unlock();
        return 0;
    }

    if(mQueue.count() == 0)
    {   
        mWait.wait(mpMutex, timeout);
    }

    if(mQueue.count() > 0)
    {   
        result = mQueue.dequeue();
    }

    mpMutex->unlock();

    return result;
}

/// This will push a value onto the queue and wake up one thread waiting
/// for a value.

bool push( const QByteArray str)
{
    if(!(mParent->isConnected()))
    {
        return false;
    }

    mpMutex->lock();

    if(mResetting)
    {
        mpMutex->unlock();
        return false ;
    }

    mQueue.enqueue(str);
    mWait.wakeAll();
    mpMutex->unlock();

    return true;
}



void resetBegin()
{
    mpMutex->lock();
    mResetting = true;
    mQueue.clear();
    mWait.wakeAll();
    mpMutex->unlock();
}



void resetEnd()
{
    delete mpMutex;
    mpMutex = new QMutex();
    mResetting = false;
}



int count()
{
    return mQueue.count ();
}





private:
    QQueue < QByteArray > mQueue;
    QMutex * mpMutex;
    QWaitCondition mWait;
    bool mResetting;
    bool mQueueEnabled;
    T * mParent;
};


#endif //__MESSAGEQUEUE_H__
